using System;
using System.IO;
using System.Collections.Generic;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;

namespace TicketSellerService
{
	public class TicketsDB {
		private const int DATA_FIELDS_LENGTH = 6;
		
		private static  TicketsDB _instance = null;
		
		private IList<Flight> flights = new List<Flight>( );
		
		private IDictionary< int, List<TicketReservation>> reservations = new Dictionary< int, List<TicketReservation>>( );
		
		private Object locker = new Object( );
		
		private static Object initLock = new Object( );

        private bool initialized = false;
		
		public enum Status { 
				RESERVED_TICKET, 
				RESERVATION_FAILED, 
				RESERVATION_CANCELLED, 
				CANCELATION_FAILED,
				FLIGHT_FULL,
                DB_NOT_INITIALIZED
		};
		
		private TicketsDB ( ) {		
		}

        public void initialize( string fileName) {
            StreamReader reader = null;
            string line = null;
            if (fileName == null)
            {
                Console.WriteLine("Flights file data couldn't be found.");
                return;
            }
            try
            {
                reader = File.OpenText(fileName);
                while ((line = reader.ReadLine()) != null)
                {
                    string[] data = line.Split(' ');
                    if (data.Length != DATA_FIELDS_LENGTH)
                    {
                        Console.WriteLine("Number of fields in the string is {0}", data.Length);
                        Console.WriteLine("Invalid input format.");
                        continue;
                    }

                    Flight flight = new Flight();
                    try
                    {
                        flight.flightNumber = Convert.ToInt32(data[0]);
                        flight.source = data[1];
                        flight.destination = data[2];
                        flight.date = Convert.ToDateTime(data[3]);
                        flight.seatsCount = Convert.ToInt32(data[4]);
                        flight.price = Convert.ToDouble(data[5]);
                        flights.Add(flight);
                    }
                    catch (System.InvalidCastException e)
                    {
                        Console.WriteLine(e.StackTrace);
                    }
                }

                initialized = true;
            }
            catch (System.IO.IOException e) {
                Console.WriteLine(e.StackTrace);
            }
            return;
        }

		public static TicketsDB getInstance( ) {
			if ( _instance == null) {
				lock( initLock) {
					if (_instance == null)
						_instance = new TicketsDB( );
				}
			}
			return _instance;
		}

		public IList<Flight> getFlights( ) {
			return flights;
		}
		
		public List<TicketReservation> getFlightReservation( int flightNum) {
			List<TicketReservation> result = null;
            if (!initialized)
                return result;
			reservations.TryGetValue( flightNum, out result);
			return result;
		}
		
		public Status makeReservation( int flightNum, DateTime date, out TicketReservation finalReservation) {
			finalReservation = null;
            if (!initialized)
            {
                return Status.DB_NOT_INITIALIZED;
            }
			lock( locker) {
				foreach( Flight flight in flights) {
					if ( flight.flightNumber != flightNum || !flight.date.Equals( date)) {
						continue;
					}
					// Check whenever we can there is a free space
					// to make new reservation for desired
					// flight.
					if ( flight.seatsCount == 0) {
						return Status.FLIGHT_FULL;
					}
					// Check if we have already created list of 
					// reservations for given flight. If not
					// then create a new one.
					if ( !reservations.ContainsKey( flightNum)) {
						reservations.Add( flightNum, new List<TicketReservation>( ));
					}
					// Process with reservation.
					List<TicketReservation> flightRsrvs = null;
					if ( reservations.TryGetValue( flightNum, out flightRsrvs)) {
						flight.seatsCount--;
						TicketReservation rsrv = new TicketReservation( flight);
						flightRsrvs.Add( rsrv);
						finalReservation = rsrv;
						return Status.RESERVED_TICKET;
					}
				}
			}
			return Status.RESERVATION_FAILED;
		}
		
		public Status cancelReservation( int reservationId) {
            if (!initialized)
            {
                return Status.DB_NOT_INITIALIZED;
            }
			lock( locker) {
				foreach ( List<TicketReservation> reservs in reservations.Values) {
					foreach( TicketReservation reserv in reservs) {
						if ( reserv.reservationId == reservationId) {
							reservs.Remove( reserv);
							reserv.flight.seatsCount++;
							return Status.RESERVATION_CANCELLED;
						}	
					}
				}
			}
			return Status.CANCELATION_FAILED;
		}		
	}
	
}

